学生学号
0122009360610
实验课成绩
实验课程名称
计算机网络与通信实验
信息学院
指导教师姓名
周颖
冷卓群
学生专业班级
电信 2002
2022
2023
学年
学期
实验教学管理基本规范
实验是培养学生动手能力、分析解决问题能力的重要环节;实验报告是反映实验教学水平
与质量的重要依据。为加强实验过程管理,改革实验成绩考核方法,改善实验教学效果,提高
学生质量,特制定实验教学管理基本规范。
1 本规范适用于理工科类专业实验课程,文、经、管、计算机类实验课程可根据具体情况参
照执行或暂不执行。
2 每门实验课程一般会包括许多实验项目,除非常简单的验证演示性实验项目可以不写实验
报告外,其他实验项目均应按本格式完成实验报告
3 实验报告应由实验预习、实验过程、结果分析三大部分组成。每部分均在实验成绩中占一
定比例。各部分成绩的观测点、考核目标、所占比例可参考附表执行。各专业也可以根
具体情况,调整考核内容和评分标准
4 学生必须在完成实验预习内容的前提下进行实验。教师要在实验过程中抽查学生预习情况,
在学生离开实验室前,检查学生实验操作和记录情况,并在实验报告第二部分教师签字栏
签名,以确保实验记录的真实性。
5 教师应及时评阅学生的实验报告并给出各实验项目成绩,完整保存实验报告。在完成所有
实验项目后,教师应按学生姓名将批改好的各实验项目实验报告装订成册,构成该实验课
程总报告,按班级交课程承担单位(实验中心或实验室)保管存档。
6 实验课程成绩按其类型采取百分制或优、良、中、及格和不及格五级评定。
附表:实验考核参考内容及标准
观测点
考核目标
成绩组成
实验预习
1 预习报告
2 提问
3 对于设计型实验,着重考查设计方案的
科学性、可行性和创新性
对实验目的和基本原
的认识程度,对实验
案的设计能力
20%
实验过程
1 是否按时参加实
2 对实验过程的熟悉程度
3 对基本操作的规范程度
4 对突发事件的应急处理能
5 实验原始记录的完整程度
6 同学之间的团结协作精神
着重考查学生的实验
度、基本操作技能;
谨的治学态度、团结
作精神
30%
结果分析
1 所分析结果是否用原始记录数据
2 计算结果是否正
3 实验结果分析是否合理
4 对于综合实验,各项内容之间是否有分
析、比较与判断
考查学生对实验数据
理和现象分析的能力
对专业知识的综合应
能力;事实求实的精神
50%
实验课程名称: 计算机网络与通信实验
基于 TCP 协议的网上聊天工具(python
实验成绩
冷卓群
专业班级
电信 2002
2
实验日期
2023/4/11
第一部分:实验预习报告包括实验目的、意义,实验基本原理与方法,
主要仪器设备及耗材,实验方案与技术路线等
一、实验目的与意义
1)了解通信协议的基本工作原理
2)设计一个基于通信协议的网络应用程序
二、实验内容
完成一个基于网络通信协议的网络应用程序开发。
具体内容可自选(如:聊天工具、爬虫工具等)
编程软件可自选(如:C语言、JAVAPython 等)
本次实验,我选择的实验内容是用 python 完成一个基于 TCP 的聊天工具
三、实验基本原理
1. 运输层协议概述
在因特网上,运输层协议为它上面的应用层提供通信服务,即运输层在网络层
的基础上,为应用进程之间的通信提供服务。
TCP/IP 运输层协议的两个主要协议都是互联网的正式标准,包括户数据报
协议 UDP (User Datagram Protocol)传输控制协议 TCP(Transmission Control
Protocol)UDP TCP 的特点在第三次实验时已经详细介绍过了,这里简单介绍
一下:
UDP 在传送数据数据之前不需要先建立连接。远地主机的运输层在收到 UDP
报文后,不需要给出任何确认。所以 UDP 是一种无连接的传输层协议,传输可靠
性没有保证。虽然 UDP 不提供可靠交付,但由于 UDP 非常简单,在某些情况下
UDP 是一种最有效的工作方式。
TCP 则提供面向连接的服务。在传送数据之前必须先建立连接(建立连接需要
经过三次握手,第一次实验已经验证过)数据传送结束后要释放连接(释放连接
需要经过四次挥手)由于 TCP 要提供可靠的、面向连接的运输服务,因此不可避
免地增加了许多的开销,如确认、流量控制、计时器以及连接管理等。这不仅使协
议数据单元的首部增大很多,还要占用许多的处理机资源。
因此,当应用程序对传输的可靠性要求不高,但是对传输速度和延迟要求较高
时,可以用 UDP 协议来替代 TCP 协议在传输层控制数据的转发。UDP 适合于实时
数据传输,如语音和视频通信,因为它们即使偶尔丢失一两个数据包,也不会对接
收结果产生太大影响。例如:我们在看视频的时候偶尔丢一两个包也不影响体验。
而对于需要可靠传输的场景,则使用 TCP 连接,比如收发文字消息。因此本次聊天
工具基于 TCP 设计。
2. 聊天工具设计原理
TCP/IP 协议的核心部分是传输层协议(TCPUDP)网络层协议(IP)和物理接口
层,这三层通常是在操作系统内核中设计。因此用户一般不涉及。TCP 是面向连接
的,通信双方保持一条通路,好比目前的电话线,使用 telnet 登陆 BBS 用的就
TCP 协议;UDP 是无连接的,通信双方都不保持对方的状态,浏览器访问 Internet
时使用的 HTTP 协议就是基于 UDP 协议的。编程时,编程界面有两种形式:①由
内核心直接提供的系统调用;②使用以库函数方式提供的各种函数。前者为核内设
计,后者为核外设计。用户服务要通过核外的应用程序才能设计,所以要使用套接
(socket)来设计。
在通信工具中,通常都采用客户端/服务器(C/S)体系结构,即客户机和服务器
结构。它是软件系统体系结构,通过它可以充分利用两端硬件环境的优势,将任务
合理分配到 Client 端和 Server 端来实现,降低了系统的通讯开销。
本次实验,我用 python 程序 FWQ_server.py 拟出一个服务器,以供客户端连
。各个客户端的代码可以完全相同,不影响通信,因此我将第一个客户端
KHD_client1.py 的代码直接复制给客户端 KHD_client2.py KHD_client3.py
图一 客户端与服务器程序结构
Socket 是一个抽象层,应用程序可以通过它发送或接收数据,可对其进行像对
文件一样的打开、读写和关闭等操作。套接字允许应用程序将 I/O 插入到网络中,
并与网络中的其他应用程序进行通信。网络套接字是 IP 地址与端口的组合。
Socket 系统调用包括创建 Socket、将创建的 Socket 与本地端口绑定、 建立
Socket 连接服务器、监听是否有连接、请求数据的可控缓冲发送和可控缓冲接收,
到最后关闭 Socket
服务器端先初始化 Socket然后与端口绑定(bind)对端口进行监听(listen),等
待客户端连接。在这时如果有个客户端初始化一个 Socket,然后连接服务器
(connect),如果连接成功,这时客户端与服务器端的连接就建立了。客户端发送数
据请求(send),服务器端接收请求并处理请求,然后把回应数据发送给客户端,客
户端读取数据,最后关闭连接,一次交互结束。具体流程图如下:
图二 客户端与服务器程序流程图
TCP 客户端:通过 connect((ip,port))请求连接,send()进行发送消息,recv()进行
接受消息。
TCP 服务:通过 bind((ip,port))绑定 ip 和端口,ip 不写则代表默认本机所
有地址,listen()控制客服端的连接数量,accept()等待阻塞,一直到客户端的到
来,recv()接受消息,send()发送消息。
第二部分:实验过程记录(可加页)(包括实验原始数据记录,实验现象
记录,实验过程发现的问题等)
一、 客户端与服务器程序
本次实验实现了简单的 TCP 聊天程序实现网上聊天,通过对服务器和端口的输
入,可以在不同的主机上运行并将其服务器与客户端连接,用多线程可以支持多人
同时聊天,客户端具有图形化用户界面,客户端之间具有群发和私聊的功能并且能
知道实时的用户在线名单,界面显示效果良好,整体完成效果良好。
图三 vscode 程序界面
TCP 服务器: 通过 bind((ip, port))绑定 ip 和端口,ip 不写则代表默认本机所有
地址,listen()控制客服端的连接数量,accept()等待阻塞,一直到客户端的到来,recv()
接受消息,send()发送消息。服务器 client python 程序 FWQ_server.py 如下:
1. import tkinter
2. import socket, threading
3.
4. win = tkinter.Tk() # 创建主窗口
5. win.title('服务器')
6. win.geometry("400x300+200+20")
7. users = {}#用户字典,也可以连接数据库
8.
9. def run(connect, addrss):
10. #接收客户端登陆的信息
11. userName = connect.recv(1024)
12. #解码并储存用户的信息
13. users[userName.decode("utf-8")] = connect
14. #在连接显示框中显示是否连接成功
15. printStr = "" + userName.decode("utf-8") + "连接\n"
16. text.insert(tkinter.INSERT, printStr)
17. #向当前登录的客户端反馈登录信息并提供在线用户列表
18. printStr ="登录成功!\n"+"当前在线的好友有:"+str(list(users.keys()))+"\n"
19. connect.send(printStr.encode())
20. #向所有在线的客户端反馈新的好友登录信息并提供在线用户列表
21. printStr = userName.decode("utf-8") + "已上线\n"+"当前在线的好友有:
"+str(list(users.keys()))+"\n"
22. for key in users:
23. if key !=userName.decode("utf-8"):
24. users[key].send(printStr.encode())
25. #接受客户端发送的信息
26. while True:
27. rData = connect.recv(1024)
28. dataStr = rData.decode("utf-8")
29. #分割字符串得到所要发送的用户名和客户端所发送的信息
30. infolist = dataStr.split(":")
31. #通过客户端要发送的信息中是否指定要发送到的用户,如果没有选择要发送的用户,则默认为群发
消息
32. if infolist[0] == "" :
33. for key in users:
34. if key !=userName.decode("utf-8"):
35. users[key].send((userName.decode("utf-8") + "说(群
发):" + infolist[1]).encode("utf"))
36. #如果接收到的消息为客户端退出函数发送的“exit”则删掉该用户在 users 字典中的信息,并通知其
他用户该用户已下线
37. elif infolist[0] == "exit":
38. del users[userName.decode("utf-8")]#删掉该用户信息
39. printStr = "" + userName.decode("utf-8") + "下线\n"
40. text.insert(tkinter.INSERT, printStr)
41. for key in users:
42. printStr = userName.decode("utf-8") + "已下线\n"+"当前在线的好友有:
"+str(list(users.keys()))+"\n"
43. users[key].send(printStr.encode())
44. #要发送信息的客户端向目标客户端发送信息
45. else:
46. if infolist[0] in users:
47. users[infolist[0]].send((userName.decode("utf-8") + "(
):" + infolist[1]).encode("utf"))
48. else:
49. printStr =infolist[0]+"不在线,上条消息未发出"+"\n"
50. connect.send(printStr.encode())
51. #界面启动按钮连接的函数
52. def startSever():
53. #启用一个线程开启服务器
54. s = threading.Thread(target=start)
55. s.start()
56. #开启线程
57.
58. def start():
59. #从输入端中获取 ip 和端口号
60. ipStr = eip.get()
61. portStr = eport.get()
62. # socket 嵌套字 TCP ipv4 和相关协议
63. server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
64. #绑定 ip 和端口号
65. server.bind((ipStr, int(portStr)))
66. #设置监听和连接的最大的数量
67. server.listen(10)
68. #服务器启动信息显示在信息窗口中
69. printStr = "服务器启动成功!\n"
70. text.insert(tkinter.INSERT, printStr)
71. #模拟服务器要一直运行所以使用死循环
72. while True:
73. #接受所连接的客户端的信息
74. connect, addrss = server.accept()
75. #每连接一个客户端就开启一个线程
76. t = threading.Thread(target=run, args=(connect, addrss))
77. t.start()
78.
79. #下面是关于界面的操作
80. labelIp = tkinter.Label(win, text='ip').grid(row=0, column=0)
81. eip = tkinter.Variable()
82.
83. labelPort = tkinter.Label(win, text='port').grid(row=1, column=0)
84. eport = tkinter.Variable()
85.
86. entryIp = tkinter.Entry(win, textvariable=eip).grid(row=0, column=1)
87. entryPort = tkinter.Entry(win, textvariable=eport).grid(row=1, column=1)
88. button = tkinter.Button(win, text="启动
", command=startSever).grid(row=1, column=2)
89. text = tkinter.Text(win, height=15, width=40)
90. labeltext = tkinter.Label(win, text='连接消息').grid(row=3, column=0)
91. text.grid(row=3, column=1)
92.
93. win.mainloop()
TCP 客户端:通过 connect((ip, port))来请求连接,send()进行发送消息,recv()
进行接受消息。客户端 client python 程序 KHD_client.py 如下:
1. import tkinter
2. import socket
3. import threading
4. import time
5.
6. win = tkinter.Tk()
7. win.title("客户端")
8. win.geometry("400x300+300+200")
9. ck = None
10.
11. def getInfo():
12. while True:
13. data = ck.recv(1024)#用于接受服务其发送的信息
14. #接收消息时同步获取系统时间并显示在消息显示框上
15. text.insert(tkinter.INSERT, time.strftime('%Y-%m-%d %H:%M:%S', time.loc
altime()) + '\n')
16. text.insert(tkinter.INSERT, data.decode("utf-8"))
17.
18. def connectServer():
19. global ck#全局
20. ipStr = eip.get()
21. portStr = eport.get()
22. userStr = euser.get()
23. client = socket.socket(socket.AF_INET, socket.SOCK_STREAM)#socked 所准守 ipv4
相关协议
24. client.connect((ipStr, int(portStr)))#连接服务器
25. client.send(userStr.encode("utf-8"))#将自己的登录名发送给服务器,函数会附带自己
IP 信息
26. ck = client
27. t = threading.Thread(target=getInfo)
28. t.start()
29.
30. def sendMail():
31. friend = None
32. friend = efriend.get()
33. sendStr = esend.get()
34. #自己发出的消息服务器不会重发会给自己,所以在客户端定义界面显示自己发送的消息
35. if friend !="":
36. text.insert(tkinter.INSERT, time.strftime('%Y-%m-%d %H:%M:%S', time.loca
ltime()) + '\n'+'我对'+friend+'说:' + sendStr+'\n')
37. else:
38. text.insert(tkinter.INSERT, time.strftime('%Y-%m-%d %H:%M:%S', time.loc
altime()) + '\n'+'(群发)说:' + sendStr+'\n')
39. #将消息发给服务器,添加分割是要方便服务器端用正则表达式分出要发送的用户名和要发
送的消息
40. sendStr = friend + ":" + sendStr+"\n"
41. ck.send(sendStr.encode("utf-8"))
42.
43. def Exit():
44. #我在服务器端定义了接收到“exit”就判定该用户下线,并删掉该用户的资料
45. sendStr = "exit" + ":" + ""
46. ck.send(sendStr.encode("utf-8"))
47. text.insert(tkinter.INSERT, "您已下线,如需接收信息请重新登录。\n")
48. #下面是界面
49. labelUse = tkinter.Label(win, text="userName").grid(row=0, column=0)
50. euser = tkinter.Variable()
51. entryUser = tkinter.Entry(win, textvariable=euser).grid(row=0, column=1)
52.
53. labelIp = tkinter.Label(win, text="服务器 ip").grid(row=1, column=0)
54. eip = tkinter.Variable()
55. entryIp = tkinter.Entry(win, textvariable=eip).grid(row=1, column=1)
56.
57. labelPort = tkinter.Label(win, text="port").grid(row=2, column=0)
58. eport = tkinter.Variable()
59. entryPort = tkinter.Entry(win, textvariable=eport).grid(row=2, column=1)
60.
61. button = tkinter.Button(win, text="登录
", command=connectServer).grid(row=0, column=2)
62.
63. text = tkinter.Text(win, height=10, width=40)
64. labeltext= tkinter.Label(win, text="显示消息").grid(row=4, column=0)
65. text.grid(row=4, column=1)
66.
67. esend = tkinter.Variable()
68. labelesend = tkinter.Label(win, text="发送的消息").grid(row=5, column=0)
69. entrySend = tkinter.Entry(win, textvariable=esend).grid(row=5, column=1)
70.
71. efriend = tkinter.Variable()
72. labelefriend= tkinter.Label(win, text="发给谁").grid(row=6, column=0)
73. entryFriend = tkinter.Entry(win, textvariable=efriend).grid(row=6, column=1)
74. button2 = tkinter.Button(win, text="发送
", command=sendMail).grid(row=6, column=2)
75. button2 = tkinter.Button(win, text="下线", command=Exit).grid(row=2, column=2)
76. win.mainloop()
77. # 注意,loop 因为是循环的意思,window.mainloop 就会让 window 不断的刷新,如果没有
mainloop,就是一个静态的 window,传入进去的值就不会有循环,mainloop 就相当于一个很大的
while 循环,有个 while,每点击一次就会更新一次,所以我们必须要有循环
78. # 所有的窗口文件都必须有类似的 mainloop 函数,mainloop 是窗口文件的关键的关键
二、 程序调试
服务器启动
运行服务器 server 的程序 FWQ_server.py,出现以下界面:
图四 服务器界面
其中第一行表示你的主机的 ipv4 地址,打开 cmd 后输入 ipconfig 可以查看电脑
ipv4 地址;第二行表示的是端口,这里最好用大一点 8080 9876 等,否则会出
现端口占用情况,这里我用 9876
本机的无线局域网适配器的 ipv4 地址为:192.168.3.46
图五 本电脑 IP 地址
ipv4 地址:192.168.3.46 和端口:9876 输入对话框,点击启动,服务器启动
成功。
图六 服务器启动
客户端启动
运行客户端 client 的程序 KHD_client.py,出现以下界面:
图七 客户端界面
这里第一行输入的是用户名,可以是任意的字符;第二行输入的是你要连接的
服务器的 ip(和上面服务器的 ip 一样,为 192.168.3.46第三行输入的是端口(和
第服务器写的端口保持一致,为 9876)。点击登录,客户端登录成功。
图八 客户端 12录成功
按同样的方法,登录第二个和第三个客户端。
图九 客户端 3登录成功
登录成功后即可开始对话和测试,本聊天工具支持群聊和私聊。
私聊功能测试
下面测试私聊功能,客户端 1“电信 2002”向客户端 2“冷卓群”发起私聊:
“你的学号是多少?”客户端 2收到信息后回复:“我的学号是 0122009360610”。
图十 私聊功能测试
由图十可以发现,私聊功能正常,双方都能正常通信,且没有延时。并且由图
十一所示,私聊时第三方:客户端 3“武汉理工大学信息学院”无法收到私聊的信
息,通信双方的隐私得到了保障。
图十一 第三方无法收到私聊信息
群聊功能测试
只要在发送信息界面的“发给谁”那一栏不填写任何信息,即可实现群聊。演
示结果如图十二所示,不过界面比较简陋,群聊和私聊都在一个对话框内。群聊时,
一个客户端发送的信息,所有客户端都可以收到这条消息。并且服务器不会保存客
户端发送的消息数据,保证了客户的隐私。
图十二 群聊功能测试(1
图十三 群聊功能测试(2
下线功能测试
当一个客户端下线后,服务器能识别到该客户端已下线,并且会通知其他客户
端该客户端已下线,此时其他客户端无法再与该客户端通信。具体见图十四。
图十四 下线功能测试(1
此时,其他客户端无法和已下线的客户端通信,已下线的客户端也收不到其他
人发送的消息。具体见图十五。
图十五 下线功能测试(2
教师签字__________
第三部分 果与讨论(可加页)
一、实验结果分析(包括数据处理、实验现象分析、影响因素讨论、
合分析和结论等
1. 实验结果分析
答:
2. 综合分析和结论
答: